import {Token} from "../tokenizer/Token";
import Tokenizer from "../tokenizer/Tokenizer";
import {TokenType} from "../tokenizer/TokenType";
import ExpectedTokenError from "../error/ExpectedTokenError";

interface Tag {
    name: string;
    value: Object;
    isList?: boolean;
}

export class Analyser {

    peekedToken: Token | undefined = undefined;
    tokenizer: Tokenizer;

    constructor(tokenizer: Tokenizer) {
        this.tokenizer = tokenizer;
    }

    /**
     * 查看下一个token
     * @private
     */
    private peek(): Token {
        if (!this.peekedToken) {
            this.peekedToken = this.tokenizer.nextToken();
        }
        return this.peekedToken;
    }


    /**
     * 获取下一个token
     * @private
     */
    private next() {
        if (this.peekedToken) {
            let token = this.peekedToken;
            this.peekedToken = undefined;
            return token;
        }

        return this.tokenizer.nextToken();
    }

    /**
     * 如果下一个 token 的类型是 tt，则返回 true
     * @param tt
     * @private
     */
    private check(tt: TokenType): boolean {
        let token = this.peek();
        return token.tokenType === tt;
    }

    /**
     * 如果下一个 token 的类型是 tt，则前进一个 token 并返回这个 token
     * @param tt
     * @private
     */
    private nextIf(tt: TokenType) {
        let token = this.peek();
        if (token.tokenType === tt) {
            return this.next();
        }
        return undefined;
    }

    /**
     * 如果下一个 token 的类型是 tt，则前进一个 token 并返回，否则抛出异常
     * @param tt
     * @private
     */
    private expect(tt: TokenType) {
        let token = this.peek();
        if (token.tokenType === tt) {
            return this.next();
        }

        throw new ExpectedTokenError(tt, token);
    }

    analyseAllTag(): Tag {
        const tag = this.analyseStart();
        if (this.check(TokenType.EndTag)) {
            this.analyseEnd();
            tag.value = "";
            return tag;
        }
        while (!this.check(TokenType.EndTag)) {
            if (this.check(TokenType.Lt)) {
                const {name, value, isList} = this.analyseAllTag();
                if (tag.value[name] && tag.value[name] instanceof Array) {
                    tag.value[name].push(value);
                } else if (tag.value[name]) {
                    tag.value[name] = [tag.value[name], value];
                } else if (isList) {
                    tag.value[name] = [value];
                } else {
                    tag.value[name] = value;
                }
            } else {
                const token = this.expect(TokenType.String);

                tag.value = token.value;
            }
        }
        this.analyseEnd();
        return tag;
    }

    private analyseStart(): Tag {
        this.expect(TokenType.Lt);
        let token = this.expect(TokenType.Ident);

        let isList: boolean = false;
        if (!this.check(TokenType.Gt)) {
            this.expect(TokenType.List);
            isList = true;
        }
        this.expect(TokenType.Gt);
        return {
            name: token.value,
            value: {} as Object,
            isList,
        } as Tag;
    }

    private analyseEnd(checkLt: boolean = true) {
        this.expect(TokenType.EndTag);
        this.expect(TokenType.Ident);
        this.expect(TokenType.Gt);
    }
}